<html>
  <head>
	  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>i5ting_ztree_toc:多线程（二）几种常见的线程池详解</title>
		<link href="toc/style/github-bf51422f4bb36427d391e4b75a1daa083c2d840e.css" media="all" rel="stylesheet" type="text/css"/>
		<link href="toc/style/github2-d731afd4f624c99a4b19ad69f3083cd6d02b81d5.css" media="all" rel="stylesheet" type="text/css"/>
		<link href="toc/css/zTreeStyle/zTreeStyle.css" media="all" rel="stylesheet" type="text/css"/>
	  <style>
		pre {
		    counter-reset: line-numbering;
		    border: solid 1px #d9d9d9;
		    border-radius: 0;
		    background: #fff;
		    padding: 0;
		    line-height: 23px;
		    margin-bottom: 30px;
		    white-space: pre;
		    overflow-x: auto;
		    word-break: inherit;
		    word-wrap: inherit;
		}

		pre a::before {
		  content: counter(line-numbering);
		  counter-increment: line-numbering;
		  padding-right: 1em; /* space after numbers */
		  width: 25px;
		  text-align: right;
		  opacity: 0.7;
		  display: inline-block;
		  color: #aaa;
		  background: #eee;
		  margin-right: 16px;
		  padding: 2px 10px;
		  font-size: 13px;
		  -webkit-touch-callout: none;
		  -webkit-user-select: none;
		  -khtml-user-select: none;
		  -moz-user-select: none;
		  -ms-user-select: none;
		  user-select: none;
		}

		pre a:first-of-type::before {
		  padding-top: 10px;
		}

		pre a:last-of-type::before {
		  padding-bottom: 10px;
		}

		pre a:only-of-type::before {
		  padding: 10px;
		}

		.highlight { background-color: #ffffcc } /* RIGHT */
		</style>
  </head>
  <body>
	  <div>
				<div style='width:25%;'>
						<ul id="tree" class="ztree" style='width:100%'>

						</ul>
				</div>
        <div id='readme' style='width:70%;margin-left:20%;'>
          	<article class='markdown-body'>
            	<h2 id="-">线程池——几种常见的线程池详解</h2>
<h3 id="5-1-fixedthreadpool">5.1 FixedThreadPool</h3>
<h5 id="fixedthreadpool-"><code>FixedThreadPool</code> 被称为<strong>可重用固定线程数的线程池。</strong></h5>
<pre><code class="lang-java">    /**
    * 创建一个可重用固定数量线程的线程池
    */
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue&lt;Runnable&gt;(),
                                      threadFactory);
    }
</code></pre>
<p>如上：有两个参数 nThreads 和 threadFactory，nThreads 为固定线程数，threadFactor用于创建新线程。</p>
<h5 id="newfixedthreadpool-">newFixedThreadPool的执行任务过程：</h5>
<ol>
<li>如果当前运行的线程数小于 corePoolSize， 如果再来新任务的话，就创建新的线程来执行任务；</li>
<li>当前运行的线程数等于 corePoolSize 后， 如果再来新任务的话，会将任务加入 <code>LinkedBlockingQueue</code>；</li>
<li>线程池中的线程执行完 手头的任务后，会在循环中反复从 <code>LinkedBlockingQueue</code> 中获取任务来执行；</li>
</ol>
<h3 id="5-2-singlethreadexecutor">5.2 SingleThreadExecutor</h3>
<h5 id="singlethreadexecutor-"><code>SingleThreadExecutor</code> 是只有一个线程的线程池。</h5>
<pre><code class="lang-java">    /**
    *返回只有一个线程的线程池
    */
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue&lt;Runnable&gt;(),
                                    threadFactory));
    }
</code></pre>
<h5 id="newfixedthreadpool-">newFixedThreadPool的执行任务过程：</h5>
<ol>
<li>如果当前运行的线程数少于 corePoolSize，则创建一个新的线程执行任务；</li>
<li>当前线程池中有一个运行的线程后，将任务加入 <code>LinkedBlockingQueue</code></li>
<li>线程执行完当前的任务后，会在循环中反复从<code>LinkedBlockingQueue</code> 中获取任务来执行；</li>
</ol>
<h3 id="5-3-">5.3 总结</h3>
<p><strong><code>FixedThreadPool</code> 和 <code>SingleThreadExecutor</code> 使用无界队列 <code>LinkedBlockingQueue</code>（队列的容量为 Integer.MAX_VALUE）作为线程池的工作队列会对线程池带来如下影响 ：</strong></p>
<ol>
<li>当线程池中的线程数达到 <code>corePoolSize</code> 后，新任务将在无界队列中等待，因此线程池中的线程数不会超过 corePoolSize；</li>
<li>由于使用无界队列时 <code>maximumPoolSize</code> 将是一个无效参数，因为不可能存在任务队列满的情况。所以，通过创建 <code>FixedThreadPool</code>的源码可以看出创建的 <code>FixedThreadPool</code> 的 <code>corePoolSize</code> 和 <code>maximumPoolSize</code> 被设置为同一个值。</li>
<li>由于 1 和 2，使用无界队列时 <code>keepAliveTime</code> 将是一个无效参数；(线程数永远不会大于corePoolSize)</li>
<li>运行中的 <code>FixedThreadPool</code> 和 <code>SingleThreadExecutor</code> （未执行 <code>shutdown()</code>或 <code>shutdownNow()</code>）不会拒绝任务，在任务比较多的时候会导致 OOM（内存溢出）。</li>
</ol>
<h3 id="6-1-cachedthreadpool">6.1  CachedThreadPool</h3>
<h5 id="cachedthreadpool-"><code>CachedThreadPool</code> 是一个会根据需要创建新线程的线程池。</h5>
<pre><code class="lang-java">    /**
     * 创建一个线程池，根据需要创建新线程，但会在先前构建的线程可用时重用它。
     */
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue&lt;Runnable&gt;(),
                                      threadFactory);
    }
</code></pre>
<h5 id="cachedthreadpool-"><code>CachedThreadPool</code>的执行任务过程：</h5>
<ol>
<li>首先执行 <code>SynchronousQueue.offer(Runnable task)</code> 提交任务到任务队列。如果当前 <code>maximumPool</code> 中有闲线程正在执行 <code>SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)</code>，那么主线程执行 offer 操作与空闲线程执行的 <code>poll</code> 操作配对成功，主线程把任务交给空闲线程执行，<code>execute()</code>方法执行完成，否则执行下面的步骤 2；</li>
<li>当初始 <code>maximumPool</code> 为空，或者 <code>maximumPool</code> 中没有空闲线程时，将没有线程执行 <code>SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)</code>。这种情况下，步骤 1 将失败，此时 <code>CachedThreadPool</code> 会创建新线程执行任务，execute 方法执行完成；</li>
</ol>
<h5 id="-cachedthreadpool-">为什么不推荐使用<code>CachedThreadPool</code>？</h5>
<p><code>CachedThreadPool</code>允许创建的线程数量为 Integer.MAX_VALUE ，可能会创建大量线程，从而导致 OOM。</p>
<h3 id="6-2-scheduledthreadpoolexecutor">6.2 ScheduledThreadPoolExecutor</h3>
<h5 id="scheduledthreadpoolexecutor-"><strong><code>ScheduledThreadPoolExecutor</code> 主要用来在给定的延迟后运行任务，或者定期执行任务。</strong></h5>
<blockquote>
<h5 id="scheduledthreadpoolexecutor-delayqueue-priorityqueue-priorityqueue-scheduledfuturetask-time-scheduledfuturetask-squencenumber-"><code>ScheduledThreadPoolExecutor</code> 使用的任务队列 <code>DelayQueue</code> 封装了一个 <code>PriorityQueue</code>(优先级队列)，<code>PriorityQueue</code> 会对队列中的任务进行排序，执行所需时间短的放在前面先被执行(<code>ScheduledFutureTask</code> 的 <code>time</code> 变量小的先执行)，如果执行所需时间相同则先提交的任务将被先执行(<code>ScheduledFutureTask</code> 的 <code>squenceNumber</code> 变量小的先执行)。</h5>
</blockquote>
<h5 id="-">执行过程</h5>
<ol>
<li>线程 1 从 <code>DelayQueue</code> 中获取已到期的 <code>ScheduledFutureTask（DelayQueue.take()）</code>。到期任务是指 <code>ScheduledFutureTask</code>的 time 大于等于当前系统的时间；</li>
<li>线程 1 执行这个 <code>ScheduledFutureTask</code>；</li>
<li>线程 1 修改 <code>ScheduledFutureTask</code> 的 time 变量为下次将要被执行的时间；</li>
<li>线程 1 把这个修改 time 之后的 <code>ScheduledFutureTask</code> 放回 <code>DelayQueue</code> 中（<code>DelayQueue.add()</code>)。</li>
</ol>
<h3 id="7-jdk1-8-newworkstealingpool-">7.JDK1.8新增的 <code>newWorkStealingPool</code> 线程池</h3>
<h5 id="newworkstealingpool-"><code>newWorkStealingPool</code>构造方法</h5>
<pre><code class="lang-java">     /**
     * Creates a thread pool that maintains enough threads to support
     * the given parallelism level, and may use multiple queues to
     * reduce contention. The parallelism level corresponds to the
     * maximum number of threads actively engaged in, or available to
     * engage in, task processing. The actual number of threads may
     * grow and shrink dynamically. A work-stealing pool makes no
     * guarantees about the order in which submitted tasks are
     * executed.
     *
     * @param parallelism the targeted parallelism level
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code parallelism &lt;= 0}
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
</code></pre>
<p>根据构造方法可以看到它是一个并行的线程池，参数中传入的是一个线程并发的数量，这里和之前就有很明显的区别，前面4种线程池都有核心线程数、最大线程数等等，而这就使用了一个并发线程数解决问题。从介绍中，还说明这个线程池不会保证任务的顺序执行，也就是 WorkStealing 的意思，抢占式的工作。</p>
<p>线程池是基于ForkJoinPool 的扩展.算法思想就是窃取算法。大概的意思就是将任务按照工作线程均分。然后先工作完的线程去帮助没处理完的线程工作。以实现最快完成工作。由于能够合理的使用CPU进行对任务操作（并行操作），所以它适合处理很耗时的线程。</p>
<h3 id="8-">8.线程池大小确定</h3>
<h5 id="-">简单并且适用面比较广的公式：</h5>
<ul>
<li><strong>CPU 密集型任务(N+1)：</strong> 这种任务消耗的主要是 CPU 资源，可以将线程数设置为 N（CPU 核心数）+1，比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断，或者其它原因导致的任务暂停而带来的影响。一旦任务暂停，CPU 就会处于空闲状态，而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。</li>
<li><strong>I/O 密集型任务(2N)：</strong> 这种任务应用起来，系统会用大部分的时间来处理 I/O 交互，而线程在处理 I/O 的时间段内不会占用 CPU 来处理，这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中，我们可以多配置一些线程，具体的计算方法是 2N。</li>
</ul>
<h5 id="-cpu-io-"><strong>如何判断是 CPU 密集任务还是 IO 密集任务？</strong></h5>
<p>CPU 密集型简单理解就是利用 CPU 计算能力的任务比如你在内存中对大量数据进行排序。单凡涉及到网络读取，文件读取这类都是 IO 密集型，这类任务的特点是 CPU 计算耗费时间相比于等待 IO 操作完成的时间来说很少，大部分时间都花在了等待 IO 操作完成上。</p>

          	</article>
        </div>
		</div>
  </body>
</html>
<script type="text/javascript" src="toc/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="toc/js/jquery.ztree.all-3.5.min.js"></script>
<script type="text/javascript" src="toc/js/ztree_toc.js"></script>
<script type="text/javascript" src="toc_conf.js"></script>

<SCRIPT type="text/javascript" >
<!--
$(document).ready(function(){
    var css_conf = eval(markdown_panel_style);
    $('#readme').css(css_conf)
    
    var conf = eval(jquery_ztree_toc_opts);
		$('#tree').ztree_toc(conf);
});
//-->
</SCRIPT>